<!-- 表格组件 -->
<!-- 支持：el-table 全部属性、事件、插槽，同官方文档写法 -->
<!-- 扩展功能：分页组件、渲染自定义列、loading、表格全局边框、斑马纹、表格尺寸、表头背景配置 -->
<!-- 获取 ref：默认暴露了 elTableRef 外部通过 ref.value.elTableRef 可以调用 el-table 方法 -->
<template>
	<div class="art-table" :class="{ 'is-empty': isEmpty }" :style="containerHeight">
		<ElTable
			ref="elTableRef"
			v-loading="!!loading"
			v-bind="{ ...$attrs, ...props, height, stripe, border, size, headerCellStyle }"
		>
			<template v-for="col in columns" :key="col.prop || col.type">
				<!-- 渲染全局序号列 -->
				<ElTableColumn v-if="col.type === 'globalIndex'" v-bind="{ ...col }">
					<template #default="{ $index }">
						<span>{{ getGlobalIndex($index) }}</span>
					</template>
				</ElTableColumn>

				<!-- 渲染展开行 -->
				<ElTableColumn v-else-if="col.type === 'expand'" v-bind="cleanColumnProps(col)">
					<template v-if="col.useSlot && col.slotName" #default="slotScope">
						<slot
							:name="col.slotName"
							v-bind="{
								...slotScope,
								value: slotScope.row || undefined
							}"
						/>
					</template>
					<template v-else #default="{ row }">
						<component :is="col.formatter ? col.formatter(row) : null"/>
					</template>
				</ElTableColumn>

				<!-- 渲染普通列 -->
				<ElTableColumn v-else v-bind="cleanColumnProps(col)">
					<template v-if="col.useHeaderSlot && col.prop" #header="headerScope">
						<slot
							:name="col.headerSlotName || `${col.prop}-header`"
							v-bind="{ ...headerScope, prop: col.prop, label: col.label }"
						>
							{{ col.label }}
						</slot>
					</template>
					<template v-if="col.useSlot && col.prop" #default="slotScope">
						<slot
							:name="col.slotName || col.prop"
							v-bind="{
								...slotScope, prop: col.prop,
								value: col.prop ? slotScope.row[col.prop] : undefined
							}"
						/>
					</template>
				</ElTableColumn>
			</template>

			<template v-if="$slots.default" #default>
				<slot/>
			</template>

			<template #empty>
				<div v-if="loading"></div>
				<ElEmpty v-else :description="emptyText" :image-size="120"/>
			</template>
		</ElTable>

		<div
			class="pagination custom-pagination"
			v-if="showPagination"
			:class="mergedPaginationOptions?.align"
			ref="paginationRef"
		>
			<ElPagination
				v-bind="mergedPaginationOptions"
				:total="pagination?.total"
				:disabled="loading"
				:page-size="pagination?.size"
				:current-page="pagination?.current"
				@size-change="handleSizeChange"
				@current-change="handleCurrentChange"
			/>
		</div>
	</div>
</template>

<script setup lang="ts">
import {ref, computed, nextTick, onMounted, onUnmounted, watch} from 'vue'
import type {ElTable, TableProps} from 'element-plus'
import {storeToRefs} from 'pinia'
import {ColumnOption} from '@/types'
import {useTableStore} from '@/store/modules/table'
import {useCommon} from '@/composables/useCommon'
import {useElementSize, useWindowSize} from '@vueuse/core'

defineOptions({name: 'ArtTable'})

const {width} = useWindowSize()
const elTableRef = ref<InstanceType<typeof ElTable> | null>(null)
const paginationRef = ref<HTMLElement>()
const tableStore = useTableStore()
const {isBorder, isZebra, tableSize, isFullScreen, isHeaderBackground} = storeToRefs(tableStore)

// 动态计算表格头部高度
const tableHeaderHeight = ref(0)

// ResizeObserver 用于监听表格头部高度变化
let resizeObserver: ResizeObserver | null = null

/** 分页配置接口 */
interface PaginationConfig {
	/** 当前页码 */
	current: number
	/** 每页显示条目个数 */
	size: number
	/** 总条目数 */
	total: number
}

/** 分页器配置选项接口 */
interface PaginationOptions {
	/** 每页显示个数选择器的选项列表 */
	pageSizes?: number[]
	/** 分页器的对齐方式 */
	align?: 'left' | 'center' | 'right'
	/** 分页器的布局 */
	layout?: string
	/** 是否显示分页器背景 */
	background?: boolean
	/** 只有一页时是否隐藏分页器 */
	hideOnSinglePage?: boolean
	/** 分页器的大小 */
	size?: 'small' | 'default' | 'large'
	/** 分页器的页码数量 */
	pagerCount?: number
}

/** ArtTable 组件的 Props 接口 */
interface ArtTableProps extends TableProps<Record<string, any>> {
	/** 加载状态 */
	loading?: boolean
	/** 列渲染配置 */
	columns?: ColumnOption[]
	/** 分页状态 */
	pagination?: PaginationConfig
	/** 分页配置 */
	paginationOptions?: PaginationOptions
	/** 空数据表格高度 */
	emptyHeight?: string
	/** 空数据时显示的文本 */
	emptyText?: string
	/** 是否开启 ArtTableHeader，解决表格高度自适应问题 */
	showTableHeader?: boolean
}

const props = withDefaults(defineProps<ArtTableProps>(), {
	columns: () => [],
	fit: true,
	showHeader: true,
	stripe: undefined,
	border: undefined,
	size: undefined,
	emptyHeight: '100%',
	emptyText: '暂无数据',
	showTableHeader: true
})

const LAYOUT = {
	MOBILE: 'prev, pager, next, sizes, jumper, total',
	IPAD: 'prev, pager, next, jumper, total',
	DESKTOP: 'total, prev, pager, next, sizes, jumper'
}

const layout = computed(() => {
	if (width.value < 768) {
		return LAYOUT.MOBILE
	} else if (width.value < 1024) {
		return LAYOUT.IPAD
	} else {
		return LAYOUT.DESKTOP
	}
})

// 默认分页常量
const DEFAULT_PAGINATION_OPTIONS: PaginationOptions = {
	pageSizes: [10, 20, 30, 50, 100],
	align: 'center',
	background: true,
	layout: layout.value,
	hideOnSinglePage: false,
	size: 'default',
	pagerCount: width.value > 1200 ? 7 : 5
}

// 合并分页配置
const mergedPaginationOptions = computed(() => ({
	...DEFAULT_PAGINATION_OPTIONS,
	...props.paginationOptions
}))

// 边框 (优先级：props > store)
const border = computed(() => props.border ?? isBorder.value)
// 斑马纹
const stripe = computed(() => props.stripe ?? isZebra.value)
// 表格尺寸
const size = computed(() => props.size ?? tableSize.value)
// 数据是否为空
const isEmpty = computed(() => props.data?.length === 0)

const {height: paginationHeight} = useElementSize(paginationRef)

// 容器高度计算
const containerHeight = computed(() => {
	let offset = 0
	if (!props.showTableHeader) {
		// 没有表格头部时，只考虑分页器高度
		offset = paginationHeight.value === 0 ? 0 : paginationHeight.value + PAGINATION_SPACING.value
	} else {
		// 有表格头部时，动态计算表格头部高度 + 分页器高度 + 间距
		const headerHeight = tableHeaderHeight.value || DEFAULT_TABLE_HEADER_HEIGHT
		const paginationOffset =
			paginationHeight.value === 0 ? 0 : paginationHeight.value + PAGINATION_SPACING.value
		offset = headerHeight + paginationOffset + TABLE_HEADER_SPACING
	}
	return {height: offset === 0 ? '100%' : `calc(100% - ${offset}px)`}
})

// 表格高度逻辑
const height = computed(() => {
	// 全屏模式下占满全屏
	if (isFullScreen.value) return '100%'
	// 空数据且非加载状态时固定高度
	if (isEmpty.value && !props.loading) return props.emptyHeight
	// 使用传入的高度
	if (props.height) return props.height
	// 默认占满容器高度
	return '100%'
})

// 表头背景颜色样式
const headerCellStyle = computed(() => ({
	background: isHeaderBackground.value
		? 'var(--el-fill-color-lighter)'
		: 'var(--art-main-bg-color)',
	...(props.headerCellStyle || {}) // 合并用户传入的样式
}))

// 是否显示分页器
const showPagination = computed(() => props.pagination && !isEmpty.value)

// 清理列属性，移除插槽相关的自定义属性，确保它们不会被 ElTableColumn 错误解释
const cleanColumnProps = (col: ColumnOption) => {
	const columnProps = {...col}
	// 删除自定义的插槽控制属性
	delete columnProps.useHeaderSlot
	delete columnProps.headerSlotName
	delete columnProps.useSlot
	delete columnProps.slotName
	return columnProps
}

// 分页大小变化
const handleSizeChange = (val: number) => {
	emit('pagination:size-change', val)
}

// 分页当前页变化
const handleCurrentChange = (val: number) => {
	emit('pagination:current-change', val)
	scrollToTop() // 页码改变后滚动到表格顶部
}

// 滚动表格内容到顶部，并可以联动页面滚动到顶部
const scrollToTop = () => {
	nextTick(() => {
		elTableRef.value?.setScrollTop(0) // 滚动 ElTable 内部滚动条到顶部
		useCommon().scrollToTop() // 调用公共 composable 滚动页面到顶部
	})
}

// 全局序号
const getGlobalIndex = (index: number) => {
	if (!props.pagination) return index + 1
	const {current, size} = props.pagination
	return (current - 1) * size + index + 1
}

const emit = defineEmits<{
	(e: 'pagination:size-change', val: number): void
	(e: 'pagination:current-change', val: number): void
}>()

// 表格头部默认高度常量
const DEFAULT_TABLE_HEADER_HEIGHT = 44
// 分页器与表格之间的间距常量（计算属性，响应 showTableHeader 变化）
const PAGINATION_SPACING = computed(() => (props.showTableHeader ? 6 : 15))
// 表格头部与表格之间的间距常量
const TABLE_HEADER_SPACING = 12

// 查找并监听表格头部高度变化
const observeTableHeader = () => {
	try {
		// 清理之前的监听
		if (resizeObserver) {
			resizeObserver.disconnect()
			resizeObserver = null
		}

		// 如果不需要显示表格头部，直接返回
		if (!props.showTableHeader) {
			tableHeaderHeight.value = 0
			return
		}

		// 查找表格头部元素
		const tableHeader = document.getElementById('art-table-header') as HTMLElement
		if (!tableHeader) {
			// 如果找不到表格头部，使用默认高度
			tableHeaderHeight.value = DEFAULT_TABLE_HEADER_HEIGHT
			return
		}

		// 初始化高度
		tableHeaderHeight.value = tableHeader.offsetHeight

		// 创建 ResizeObserver 监听高度变化
		resizeObserver = new ResizeObserver((entries) => {
			for (const entry of entries) {
				if (entry.target === tableHeader) {
					tableHeaderHeight.value = entry.contentRect.height
				}
			}
		})

		resizeObserver.observe(tableHeader)
	} catch (error) {
		console.warn('监听表格头部高度失败:', error)
		// 出错时使用默认高度
		tableHeaderHeight.value = DEFAULT_TABLE_HEADER_HEIGHT
	}
}

// 组件挂载后查找表格头部
onMounted(() => {
	nextTick(() => {
		observeTableHeader()
	})
})

// 监听数据变化和表格头部显示状态变化，重新观察表格头部
watch(
	[() => props.data, () => props.showTableHeader],
	() => {
		nextTick(() => {
			observeTableHeader()
		})
	},
	{flush: 'post'}
)

// 组件卸载时清理 ResizeObserver
onUnmounted(() => {
	if (resizeObserver) {
		resizeObserver.disconnect()
		resizeObserver = null
	}
})

defineExpose({
	scrollToTop,
	elTableRef
})
</script>

<style lang="scss" scoped>
@use './style';
</style>
